#include "Logic.h"
#include "global.h"
#include "../log.h"
#include "NetStruct.h"
#include "../GrpcClient/StatusGrpcClient.h"
#include "../DB/MySql/MySqlConPool.h"
#include "../DB/MySql/UserDao.h"
#include "../Singleton.h"
#include "../NetGlobal.h"
#include <string>


Logic::Logic()
: _is_stop(false)
{
    _worker_thread = std::thread([this](){
        this->_Process();
    });

    _InitCallback();
}

Logic::~Logic()
{
    _is_stop = true;
    if(_worker_thread.joinable())
    {
        _cv.notify_one();
        _worker_thread.join();
    }
}

void Logic::_InitCallback()
{
    _fun_callbacks.insert(std::make_pair(MSG_CHAT_LOGIN, [](std::shared_ptr<Session> session, const uint32_t &msg_id, const std::string &msg_data){
        LoginHandler(session, msg_id, msg_data);
    }));
}

void Logic::_Process()
{
    while(true)
    {
        std::unique_lock<std::mutex> lock(_queue_mutex);
        _cv.wait(lock, [this](){
            return !_msg_queue.empty() || _is_stop;
        });

        while(!_msg_queue.empty())
        {
            auto msg_node = _msg_queue.front();
            auto find = _fun_callbacks.find(msg_node->_recv_node->GetMsgId());
            if(find == _fun_callbacks.end())
            {
                log_error("msg id: {} not found", msg_node->_recv_node->GetMsgId());
                continue;
            }

            find->second(msg_node->_session, msg_node->_recv_node->GetMsgId(), 
                std::string(msg_node->_recv_node->_data.get(), msg_node->_recv_node->_total_len));
            _msg_queue.pop();
        }

        if(_is_stop) break;
    }
}

void Logic::PostMsgToQueue(std::shared_ptr<LogicNode> msg)
{
    std::lock_guard<std::mutex> lock(_queue_mutex);
    if(_msg_queue.size() > MAX_RECV_QUEUE)
    {
        log_error("msg queue is full, size: {}", _msg_queue.size());
        return;
    }

    _msg_queue.push(msg);
    if(_msg_queue.size() == 1)
    {
        _cv.notify_one();
    }
}

template<class T>
void sendResponse(std::shared_ptr<Session> conn, T& stu, const int code, const std::string& msg, const uint32_t& msg_id)
{
    stu.code = code;
    stu.msg = msg;
    std::string body = ToString(serialize(stu));
    log_debug("send response: {}", body);
    conn->Send(body.c_str(), body.size(), msg_id);
}


void LoginHandler(std::shared_ptr<Session> session, const uint32_t &msg_id, const std::string &msg_data)
{
    log_debug("login handler msg_id:{}\tmsg_data: {}",msg_id, msg_data);

    UserLoginAck ack;

    auto req = deserialize<UserLoginReq>(FromString(msg_data));
    if (std::holds_alternative<std::string_view>(req)) 
    {
        log_error("Failed to deserialize LoginReq: {}", std::get<std::string_view>(req));
        sendResponse(session, ack, ErrorCode::JsonParseError, std::string(std::get<std::string_view>(req)), msg_id);
        return;
    }
    UserLoginReq obj = std::get<UserLoginReq>(req);

    // 向状态服务器查询 token 是否有效
    auto rsp = Singleton<StatusGrpcClient>::GetInstance()->Login(obj.id, obj.token);
    if(rsp.code() != ErrorCode::Success)
    {
        log_error("Login failed, code: {}, msg: {}", rsp.code(), rsp.msg());
        sendResponse(session, ack, rsp.code(), rsp.msg(), msg_id);
        return;
    }
    log_debug("id:{}, token:{}", rsp.uid(), rsp.token());

    // 判断数据库中是否存在该用户
    UserDao userDao(Singleton<MySqlPool>::GetInstance());
    auto user = userDao.GetUserById(std::to_string(obj.id));
    if(user.empty())
    {
        log_error("User not exists");
        sendResponse(session, ack, ErrorCode::UserNotExists, "User not exists", msg_id);
        return;
    }

    ack.name = user["name"];
    ack.token = rsp.token();
    ack.id = rsp.uid();
    sendResponse(session, ack, ErrorCode::Success, "Login success", msg_id);
}